经典前端面试题-作用域 闭包 promise实现for循环不同值输出

记录最近学习到的一道经典面试题

1.要求:输出55555

原理:JS是单线程,程序在主线程中执行,当遇到setTimeout,将其放入等待队列,等for循环执行完毕后,再将等待队列中的console.log(i)执行

1
2
3
4
5
for (var i=0;i<5;i++) {
setTimeout(function(){
console.log(i)
},1000)
}

2.要求:输出501234

利用自执行/立即执行方式,闭包

原理:i传给了立即执行函数,立即执行函数的参数j是从i传过来的,而立即执行中的函数setTimeout里面的函数参数j是父函数立即执行函数的参数,这样形成一个闭包。

1
2
3
4
5
6
7
8
for (var i=0;i<5;i++) {
(function(j){
setTimeout(function(){
console.log(j)
},1000)
})(i)
}
console.log(i) //这一句是主线程中执行的 所以i是5,先执行

3.要求:输出501234

函数方式

原理:与上一条一样的,还是闭包,只是将立即执行函数单独提到外面

1
2
3
4
5
6
7
8
9
var output = function(i){
setTimeout(function(){
console.log(i)
},1000)
}
for(var i=0;i<5;i++){
output(i)
}
console.log(i) //这一句是主线程中执行的 所以i是5,先执行

4.要求:输出401234

let方式

原理:作用域问题,let作为ES6新增命令,其涉及到的相关知识或许很多人也没搞清楚。

本来的js是没有块级作用域的,用var定义i时,虽然在for循环中定义,但在执行时,依然会被提升。当执行console.log(i)时,循环已经完成,这时的i=5;所以再去执行console.log(i),i=5,这也可以解释第一个为什么输出五个5。

而用let定义i,for每执行一次就生成一个块级作用域,给setTimeout

1
2
3
4
5
6
7
8
var j=0
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000)
j=i
}
console.log(j) //这里是把i的值拿出来给j,如果直接打印i会报错

5.要求:输出012345

setTimeout

原理:利用输出时间改变输出顺序,按i递增而设置延时500*i

1
2
3
4
5
6
7
8
9
10
11
for (var i=0;i<5;i++){
(function(j){
setTimeout(function(){
console.log(j)
},500*j)
})(i)
}

setTimeout(function(){
console.log(i)
},500*i) //如果不放在setTimeout,会首先打印5

6.要求:输出012345

Promise+立即执行函数

原理:定义多个异步任务,Promise也是ES6新增的,可以说是一个重要的知识点了,立下flag有空了整理下相关知识。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const tasks=[];
for (var i=0;i<5;i++){
((j)=>{
tasks.push(new Promise((resolve)=>{ //数组中按顺序执行的 setTimeout可以省略
setTimeout(()=>{
console.log(j)
resolve()
},500*j)
}))
})(i) //01234
}
//Promise.all等待所有异步任务执行完成 然后.then
Promise.all(tasks).then(()=>{
console.log(i) //i=5
})

7.要求:输出012345

Promise+函数方式实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const tasks = []
var output = function(i){
new Promise((resolve)=>{ //数组中按顺序执行的 setTimeout可以省略
setTimeout(()=>{
console.log(i)
resolve()
},500*i)
})

}

for (var i=0;i<5;i++){
tasks.push(output(i)) //task中多个异步任务
}
Promise.all(tasks).then(()=>{
setTimeout(function(){
console.log(i)
},500*i) //i=5 异步任务完成后执行
})